package cn.edu.sjtu;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import cn.edu.sjtu.dfa.CJump;
import cn.edu.sjtu.dfa.Instruction;
import cn.edu.sjtu.dfa.Jump;
import cn.edu.sjtu.dfa.Label;

public class MyCFGBuilder {
	
	HashMap<String, HashMap<String,List<Instruction>>> cfgMap;
	
	public MyCFGBuilder() {
		this.cfgMap = DataFlowAnalyzer.ils;
	}

	/*public void analyse(String path) throws IOException {
		File temp, files[];
		LinkedList<File> list = new LinkedList<File>();
		list.add(new File(path));
		while (!list.isEmpty()) {
			temp = list.removeFirst();
			if (temp.isDirectory() && !(temp.getAbsolutePath().equals("E:\\Temp\\ContentProviderTest\\dfa\\android"))) {
				files = temp.listFiles();
				if (files == null)
					continue;
				for (File f : files) {
					if (f.isDirectory())
						list.add(f);
					else
						analyseDDXFile(f.getAbsolutePath());
				}
			}
			else {
				if (!(temp.getAbsolutePath().equals("E:\\Temp\\ContentProviderTest\\dfa\\android")))
					analyseDDXFile(temp.getAbsolutePath());
			}
		}
	}

	private void analyseDDXFile(String path) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader(path));
		String s;
		String curClassName = null;
		String curMethodName = null;
		int mStartCount = 0;
		int mEndCount = 0;
		boolean addFlag = false, invokeFlag = false;
		while ((s = br.readLine()) != null) {
			if (s.startsWith(".class")) {
				String[] temp = s.split(" ");
				curClassName = temp[temp.length - 1].replace('/', '.'); 
			} else if (s.startsWith(".method")) {
				s = s.trim().substring(8);
				String[] ta = s.split(" ");
				curMethodName = ta[ta.length - 1].substring(0, ta[ta.length - 1].lastIndexOf(")") + 1);
				mStartCount = mEndCount;
			} else if (s.startsWith(".FilePos")) {
				addFlag = true;
			} else if (s.startsWith(".end method")) {
				setCFGMap(curClassName, curMethodName, mStartCount, mEndCount);
			} else {
				if (s.startsWith("\tmove-result")) {
					addFlag = false;
				} else if (addFlag) {
					if (!(s.contains("return-void")) && !(s.contains("check-cast")))
						mEndCount++;
					addFlag = false;
				}
			} 
		}
	}

	private void setCFGMap(String cName, String mName,
			int s, int e) {
		HashMap<String, List<Instruction>> thm;
		List<Instruction> tmp;
		if (cfgMap.containsKey(cName)) {
			thm = cfgMap.get(cName);
		} else {
			thm = new HashMap<String, List<Instruction>>();
		}
		if (thm.containsKey(mName)) {
			tmp = thm.get(mName);
		} else {
			tmp = new LinkedList<Instruction>();
		}
		int sp = DataFlowAnalyzer.ils.get(cName).size();
		tmp.addAll(DataFlowAnalyzer.ils.get(cName).subList(s, e));
		thm.put(mName, tmp);
		cfgMap.put(cName, thm);
	}*/

	public void outputDOTFile() throws IOException {
		String base = "graph\\";
		String document = null;
		String file = null;
		String[] t;
		HashMap<String, List<Instruction>> tm = null;
		Iterator i = cfgMap.entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry e1 = (Map.Entry)i.next();
			t = ((String)e1.getKey()).split("\\.");
			document = t[t.length - 1];
			tm = (HashMap<String, List<Instruction>>)e1.getValue();
			
			Iterator j = tm.entrySet().iterator();
			while (j.hasNext()) {
				Map.Entry e2 = (Map.Entry)j.next();
				file = (String)e2.getKey();
				t = file.split(" ");
				file = t[t.length - 1].substring(0, t[t.length - 1].lastIndexOf(")") + 1);
				List<Instruction> l = (List<Instruction>)e2.getValue();
				File f = new File(base + document);
				if (!f.exists())
					f.mkdirs();
				if (file.contains("<")) {
					file = file.replace("<", "");
					file = file.replace(">", "");
				}
				if (file.contains("/")) {
					file = file.replace("/", ".");
				}
				PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(base + document + "\\" + file + ".dot")));
				out.println("digraph G {");
				GraphBuilder gb = new GraphBuilder(l);
				gb.buildGraph();
				if(l != null){
					if(!gb.predecessors.isEmpty()){
						for(int x = 0; x < gb.predecessors.size(); x++){
							String label = null;
							String temp = l.get(x).getDexerString();
							if (temp == null){
								l.get(x).setDexerString(l.get(x).toString());
								temp = l.get(x).getDexerString();
							}
							String ts = temp.substring(temp.lastIndexOf("\n") + 1);
							label = l.get(x).toString() + "\\n" + ts.replace(" , ", "\\n");
							if ( x < 26 )
								out.println("\t" + String.valueOf((char)('a' + x)) + "[label=\"" + label + "\"];");
							else if ( x >= 26 && x < 52)
								out.println("\t" + "a" + String.valueOf((char)('a' + x)) + "[label=\"" + label + "\"];");
							else if ( x >= 52 && x < 78)
								out.println("\t" + "b" + String.valueOf((char)('a' + x)) + "[label=\"" + label + "\"];");
							else
								out.println("\t" + "c" + String.valueOf((char)('a' + x)) + "[label=\"" + label + "\"];");
						}
						for(int x = 0; x < gb.predecessors.size(); x++){
							ArrayList<Integer> tmp = gb.predecessors.get(x);
							if(!tmp.isEmpty()){
								for(int y = 0; y < tmp.size(); y++){
									String yIndex = null;
									if ( tmp.get(y) < 26 )
										yIndex = String.valueOf((char)('a' + tmp.get(y)));
									else if ( tmp.get(y) >= 26 && tmp.get(y) < 52)
										yIndex = "a" + String.valueOf((char)('a' + tmp.get(y)));
									else if ( tmp.get(y) >= 52 && tmp.get(y) < 78)
										yIndex = "b" + String.valueOf((char)('a' + tmp.get(y)));
									else
										yIndex = "c" + String.valueOf((char)('a' + tmp.get(y)));
									if ( x < 26 )
										out.println("\t" + yIndex + " -> " + String.valueOf((char)('a' + x)));
									else if ( x >= 26 && x < 52)
										out.println("\t" + yIndex + " -> " + "a" + String.valueOf((char)('a' + x)));
									else if ( x >= 52 && x < 78)
										out.println("\t" + yIndex + " -> " + "b" + String.valueOf((char)('a' + x)));
									else
										out.println("\t" + yIndex + " -> " + "c" + String.valueOf((char)('a' + x)));
									
								}
							}
						}
					}
					else{
						System.out.println("Empty Predecessors Mapping!");
					}
				}
				/*for (int x = 0; x < l.size(); x++) {
					String label = null;
					String temp = l.get(x).getDexerString();
					if (temp == null)
						continue;
					String ts = temp.substring(temp.lastIndexOf("\n") + 1);
					label = l.get(x).toString() + "\\n" + ts.replace(" , ", "\\n");
					out.println("\t" + String.valueOf((char)('a' + x)) + "[label=\"" + label + "\"];");
				}
				for (int x = 0; x < l.size(); x++) {
					if (l.get(x).toString().contains("Cjump")) {
						out.println("\t" + String.valueOf((char)('a' + x)) + " -> " + String.valueOf((char)('a' + x + 1)));
						Label l1 = ((CJump)l.get(x)).getLabel1();
						for (int y = x + 1; y < l.size(); y++) {
							if (l.get(y).getFilePosition() == l1.getLabel()) {
								if (x != l.size() - 1)
									out.println("\t" + String.valueOf((char)('a' + x)) + " -> " + String.valueOf((char)('a' + y)));
							}
						}
					} else if (l.get(x).toString().contains("goto")) {
						Label l1 = ((Jump)l.get(x)).getLabel();
						for (int y = x - 1; y >= 0; y--) {
							if (l.get(y).getFilePosition() == l1.getLabel()) {
								out.println("\t" + String.valueOf((char)('a' + x)) + " -> " + String.valueOf((char)('a' + y)));
							}
						}
					} else {
						//if (x != l.size() - 1 && DataFlowAnalyzer.filePos2Line.get(l.get(x).getFilePosition()) <= DataFlowAnalyzer.filePos2Line.get(l.get(x + 1).getFilePosition()))
							out.println("\t" + String.valueOf((char)('a' + x)) + " -> " + String.valueOf((char)('a' + x + 1)));
					}
				}*/
				out.println("}");
				out.close();
			}
		}
	}

	public void printCFG(String path) {
		File temp, files[];
		LinkedList<File> list = new LinkedList<File>();
		list.add(new File(path));
		while (!list.isEmpty()) {
			temp = list.removeFirst();
			if (temp.isDirectory()) {
				files = temp.listFiles();
				if (files == null)
					continue;
				for (File f : files) {
					if (f.isDirectory())
						list.add(f);
					else {
						String strcmd = "cmd /c start dot " + f.getAbsolutePath()
								+ " -T png -o "  + f.getAbsolutePath().replace(".dot", ".png");
						try {
							Process i= Runtime.getRuntime().exec(strcmd);
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			}
			else {
				String strcmd = "cmd /c start dot " + temp.getAbsolutePath()
						+ " -T png -o "  + temp.getAbsolutePath().replace(".dot", ".png");
				try {
					Process i= Runtime.getRuntime().exec(strcmd);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
